using SSPivot;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;


namespace System
{
    /// <summary>
    /// string 扩展
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// 判断字符串是否为null
        /// </summary>
        /// <param name="this">要检查的字符串</param>
        /// <returns>如果字符串为null返回true，否则返回false</returns>
        public static bool IsNull(this string @this)
        {
            return @this == null;
        }
        /// <summary>
        /// 判断字符串是否为null或空字符串
        /// </summary>
        /// <param name="this">要检查的字符串</param>
        /// <returns>如果字符串为null或空字符串("")返回true，否则返回false</returns>
        public static bool IsNullOrEmpty(this string @this) => string.IsNullOrEmpty(@this);

        /// <summary>
        /// 判断字符串是否不为null且不为空字符串
        /// </summary>
        /// <param name="this">要检查的字符串</param>
        /// <returns>如果字符串不为null且不为空字符串("")返回true，否则返回false</returns>
        public static bool IsNotNullOrEmpty(this string @this)
            => !string.IsNullOrEmpty(@this);

        /// <summary>
        /// 判断字符串是否为null、空字符串或仅包含空白字符
        /// </summary>
        /// <param name="this">要检查的字符串</param>
        /// <returns>如果字符串为null、空字符串("")或仅包含空白字符返回true，否则返回false</returns>
        public static bool IsNullOrWhiteSpace(this string @this) => string.IsNullOrWhiteSpace(@this);

        /// <summary>
        /// 判断字符串是否不为null、不为空字符串且包含非空白字符
        /// </summary>
        /// <param name="this">要检查的字符串</param>
        /// <returns>如果字符串不为null、不为空字符串("")且包含非空白字符返回true，否则返回false</returns>
        public static bool IsNotNullOrWhiteSpace(this string @this) => !string.IsNullOrWhiteSpace(@this);

        /// <summary>
        /// 使用指定分隔符拆分字符串
        /// </summary>
        /// <param name="str">要拆分的字符串</param>
        /// <param name="separator">分隔符字符串</param>
        /// <returns>包含子字符串的数组</returns>
        /// <exception cref="ArgumentNullException">当str为null时抛出</exception>
        public static string[] Split(this string str, string separator)
        {
            return str.Split(new[] { separator }, StringSplitOptions.None);
        }

        /// <summary>
        /// 使用指定分隔符和选项拆分字符串
        /// </summary>
        /// <param name="str">要拆分的字符串</param>
        /// <param name="separator">分隔符字符串</param>
        /// <param name="options">拆分选项，如是否移除空条目</param>
        /// <returns>包含子字符串的数组</returns>
        /// <exception cref="ArgumentNullException">当str为null时抛出</exception>
        public static string[] Split(this string str, string separator, StringSplitOptions options)
        {
            return str.Split(new[] { separator }, options);
        }

        /// <summary>
        /// 将字符串转换为枚举值
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="value">要转换的字符串值</param>
        /// <returns>转换后的枚举值</returns>
        /// <exception cref="ArgumentNullException">当value为null时抛出</exception>
        /// <exception cref="ArgumentException">当T不是枚举类型或value不是有效的枚举值时抛出</exception>
        public static T ToEnum<T>(this string value)
            where T : struct
        {
            Check.NotNull(value, nameof(value));
            return (T)Enum.Parse(typeof(T), value);
        }


        /// <summary>
        /// 从字符串右侧截取指定长度的子字符串
        /// </summary>
        /// <param name="str">源字符串</param>
        /// <param name="len">要截取的长度</param>
        /// <returns>右侧指定长度的子字符串</returns>
        /// <exception cref="ArgumentException">当len大于字符串长度时抛出</exception>
        /// <exception cref="ArgumentNullException">当str为null时抛出</exception>
        public static string Right([NotNull] this string str, int len)
        {
            if (str.Length < len)
            {
                throw new ArgumentException("长度参数不能大于给定字符串的长度!");
            }
            return str.Substring(str.Length - len, len);
        }
        /// <summary>
        /// 从字符串左侧截取指定长度的子字符串
        /// </summary>
        /// <param name="str">源字符串</param>
        /// <param name="len">要截取的长度</param>
        /// <returns>左侧指定长度的子字符串</returns>
        /// <exception cref="ArgumentException">当len大于字符串长度时抛出</exception>
        /// <exception cref="ArgumentNullException">当str为null时抛出</exception>
        public static string Left([NotNull] this string str, int len)
        {
            if (str.Length < len)
            {
                throw new ArgumentException("长度参数不能大于给定字符串的长度!");
            }
            return str.Substring(0, len);
        }

        /// <summary>
        /// 使用指定分隔符连接集合元素
        /// </summary>
        /// <typeparam name="T">集合元素类型</typeparam>
        /// <param name="separator">分隔符字符串</param>
        /// <param name="values">要连接的集合</param>
        /// <returns>
        /// 包含集合元素的字符串，元素之间用分隔符连接
        /// 如果集合为空，返回空字符串
        /// </returns>
        /// <exception cref="ArgumentNullException">当separator或values为null时抛出</exception>
        public static string Join<T>([NotNull] this string separator, IEnumerable<T> values) => string.Join(separator, values);
        /// <summary>
        /// 将字符串分割为数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="split"></param>
        /// <returns></returns>
        public static List<T> Split<T>([NotNull] this string source, string separator = ",")
        {
            List<T> result = new List<T>();
            try
            {
                var stringArray = source.Split(separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
                stringArray.ForEach(e =>
                {
                    result.Add((T)Convert.ChangeType(e, typeof(T)));
                });
            }
            catch
            { }
            return result;
        }

        /// <summary>
        ///   转换为字节数组
        /// </summary>
        /// <param name="this">The @this to act on.</param>
        /// <returns>@this as a byte[].</returns>
        public static byte[] ToByteArray([NotNull] this string @this) => Encoding.UTF8.GetBytes(@this);

        /// <summary>
        ///   转换为字节数组
        /// </summary>
        /// <param name="this">The @this to act on.</param>
        /// <param name="encoding">encoding</param>
        /// <returns>@this as a byte[].</returns>
        public static byte[] ToByteArray([NotNull] this string @this, Encoding encoding) => encoding.GetBytes(@this);
        /// <summary>
        /// 将base64String反序列化到流，或保存成文件
        /// </summary>
        /// <param name="base64String"></param>
        /// <param name="savePath">如果为null则不保存</param>
        /// <returns></returns>
        public static Stream GetStreamFromBase64String(this string base64String, string savePath)
        {
            byte[] bytes = Convert.FromBase64String(base64String);

            var memoryStream = new MemoryStream(bytes, 0, bytes.Length);
            memoryStream.Write(bytes, 0, bytes.Length);

            if (!string.IsNullOrEmpty(savePath))
            {
                memoryStream.SaveFileFromStream(savePath);
            }

            memoryStream.Seek(0, SeekOrigin.Begin);
            return memoryStream;
        }

        /// <summary>
        /// 【异步方法】将base64String反序列化到流，或保存成文件
        /// </summary>
        /// <param name="base64String"></param>
        /// <param name="savePath">如果为null则不保存</param>
        /// <returns></returns>
        public static async Task<Stream> GetStreamFromBase64StringAsync(this string base64String, string savePath)
        {
            byte[] bytes = Convert.FromBase64String(base64String);

            var memoryStream = new MemoryStream(bytes, 0, bytes.Length);
            await memoryStream.WriteAsync(bytes, 0, bytes.Length);

            if (!string.IsNullOrEmpty(savePath))
            {
                await memoryStream.SaveFileFromStreamAsync(savePath);
            }

            memoryStream.Seek(0, SeekOrigin.Begin);
            return memoryStream;
        }
        /// <summary>
        /// 截取字符串
        /// </summary>
        /// <param name="this"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static string SafeSubstring([NotNull] this string @this, int startIndex)
        {
            if (startIndex < 0 || startIndex > @this.Length)
            {
                return string.Empty;
            }
            return @this.Substring(startIndex);
        }

        /// <summary>
        /// 截取字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="startIndex"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string SafeSubstring([NotNull] this string str, int startIndex, int length)
        {
            if (startIndex < 0 || startIndex >= str.Length || length < 0)
            {
                return string.Empty;
            }
            return str.Substring(startIndex, Math.Min(str.Length - startIndex, length));
        }

        /// <summary>
        /// 截取字符串，支持负数
        /// </summary>
        /// <param name="this">string to be handled</param>
        /// <param name="startIndex">startIndex to substract</param>
        /// <returns>substring</returns>
        public static string Sub([NotNull] this string @this, int startIndex)
        {
            if (startIndex >= 0)
            {
                return @this.SafeSubstring(startIndex);
            }
            if (Math.Abs(startIndex) > @this.Length)
            {
                return string.Empty;
            }
            return @this.Substring(@this.Length + startIndex);
        }

        #region 实用工具
        /// <summary>
        /// 计算密码强度
        /// </summary>
        /// <param name="password">密码字符串</param>
        /// <returns></returns>
        public static bool PasswordStrength(string password)
        {
            //空字符串强度值为0
            if (string.IsNullOrEmpty(password)) return false;

            //字符统计
            int iNum = 0, iLtt = 0, iSym = 0;
            foreach (char c in password)
            {
                if (c >= '0' && c <= '9') iNum++;
                else if (c >= 'a' && c <= 'z') iLtt++;
                else if (c >= 'A' && c <= 'Z') iLtt++;
                else iSym++;
            }

            if (iLtt == 0 && iSym == 0) return false; //纯数字密码
            if (iNum == 0 && iLtt == 0) return false; //纯符号密码
            if (iNum == 0 && iSym == 0) return false; //纯字母密码

            if (password.Length >= 6 && password.Length < 16) return true;//长度不大于6的密码

            if (iLtt == 0) return true; //数字和符号构成的密码
            if (iSym == 0) return true; //数字和字母构成的密码
            if (iNum == 0) return true; //字母和符号构成的密码

            return true; //由数字、字母、符号构成的密码
        }
        #endregion
        #region regex
        /// <summary>
        ///  指定的正则表达式是否在指定的输入字符串中找到匹配项
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <returns>true if the regular expression finds a match; otherwise, false.</returns>
        public static bool IsMatch([NotNull] this string input, string pattern) => Regex.IsMatch(input, pattern);
        public static string Match(this string value, string pattern)
        {
            if (value == null)
            {
                return null;
            }
            return Regex.Match(value, pattern).Value;
        }

        public static IEnumerable<string> Matches(this string value, string pattern)
        {
            if (value == null)
            {
                return new string[0];
            }
            MatchCollection source = Regex.Matches(value, pattern);
            return from Match match in source
                   select match.Value;
        }

        public static string MatchFirstNumber(this string value)
        {
            MatchCollection matchCollection = Regex.Matches(value, "\\d+");
            if (matchCollection.Count == 0)
            {
                return string.Empty;
            }
            return matchCollection[0].Value;
        }

        public static string MatchLastNumber(this string value)
        {
            MatchCollection matchCollection = Regex.Matches(value, "\\d+");
            if (matchCollection.Count == 0)
            {
                return string.Empty;
            }
            return matchCollection[matchCollection.Count - 1].Value;
        }

        public static IEnumerable<string> MatchNumbers(this string value)
        {
            return value.Matches("\\d+");
        }

        public static bool IsMatchNumber(this string value)
        {
            return value.IsMatch("\\d");
        }

        public static bool IsMatchNumber(this string value, int length)
        {
            Regex regex = new Regex("^\\d{" + length + "}$");
            return regex.IsMatch(value);
        }

        public static bool IsEmail(this string value)
        {
            return value.IsMatch("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$");
        }

        public static bool IsIpAddress(this string value)
        {
            return value.IsMatch("^((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))$");
        }

        public static bool IsNumber(this string numberInput)
        {
            return numberInput.IsMatch("^\\d+(.\\d)?$");
        }

        public static bool IsNumeric(this string value)
        {
            return value.IsMatch("^\\-?[0-9]+$");
        }

        public static bool IsUnicode(this string value)
        {
            return value.IsMatch("^[\\u4E00-\\u9FA5\\uE815-\\uFA29]+$");
        }
        /// <summary>
        /// 去除特殊字符
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public static string RemoveSpecialString(this string content)
        {
            string pattern = "([\\W]+?)";
            return Regex.Replace(content, pattern, string.Empty, RegexOptions.IgnoreCase);
        }
        /// <summary>
        /// 去除空格
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public static string RemoveSpaceString(this string content)
        {
            string pattern = "([\\s]+?)";
            return (content == null) ? null : Regex.Replace(content, pattern, string.Empty, RegexOptions.IgnoreCase);
        }
        public static string RemoveLinkString(this string content)
        {
            return Regex.Replace(content, "<a[^>]*>[^><]*</a[^>]*>", string.Empty, RegexOptions.IgnoreCase);
        }

        public static string RemoveLink(this string content)
        {
            return Regex.Replace(content, "(<a[^>]*>|</a[^>]*>)", string.Empty, RegexOptions.IgnoreCase);
        }
        /// <summary>
        /// 是否身份证
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsIdentityCardId(this string value)
        {
            if (value.Length != 15 && value.Length != 18)
            {
                return false;
            }
            DateTime result;
            Regex regex;
            string[] array;
            if (value.Length == 15)
            {
                regex = new Regex("^(\\d{6})(\\d{2})(\\d{2})(\\d{2})(\\d{3})_");
                if (!regex.Match(value).Success)
                {
                    return false;
                }
                array = regex.Split(value);
                return DateTime.TryParse(string.Format("{0}-{1}-{2}", "19" + array[2], array[3], array[4]), out result);
            }
            regex = new Regex("^(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9Xx])$");
            if (!regex.Match(value).Success)
            {
                return false;
            }
            array = regex.Split(value);
            if (!DateTime.TryParse($"{array[2]}-{array[3]}-{array[4]}", out result))
            {
                return false;
            }
            string[] array2 = (from m in value.ToCharArray()
                               select m.ToString()).ToArray();
            int[] array3 = new int[17]
            {
                7,
                9,
                10,
                5,
                8,
                4,
                2,
                1,
                6,
                3,
                7,
                9,
                10,
                5,
                8,
                4,
                2
            };
            int num = 0;
            for (int i = 0; i < 17; i++)
            {
                int num2 = int.Parse(array2[i]);
                num += num2 * array3[i];
            }
            int index = num % 11;
            string text = "10X98765432";
            string b = text.ToCharArray().ElementAt(index).ToString();
            return array2.Last().ToUpper() == b;
        }
        /// <summary>
        /// 是否手机
        /// </summary>
        /// <param name="value"></param>
        /// <param name="isRestrict"></param>
        /// <returns></returns>
        public static bool IsMobileNumber(this string value, bool isRestrict = false)
        {
            string pattern = isRestrict ? "^[1][3-9]\\d{9}$" : "^[1]\\d{10}$";
            return value.IsMatch(pattern);
        }
        /// <summary>
        /// 隐藏手机号码
        /// </summary>
        /// <param name="mobile"></param>
        /// <returns></returns>
        public static string MaskMobile(this string mobile)
        {
            if (string.IsNullOrEmpty(mobile) || mobile.Length < 11)
            {
                return mobile;
            }
            return $"{mobile.SafeSubstring(3)}****{mobile.Sub(-4)}";
        }
        /// <summary>
        /// 隐藏身份证信息
        /// </summary>
        /// <param name="idCard"></param>
        /// <returns></returns>
        public static string MaskIdCard(this string idCard)
        {
            if (string.IsNullOrEmpty(idCard) || idCard.Length < 18)
            {
                return idCard;
            }
            return $"{idCard.SafeSubstring(3)}***********{idCard.Sub(-4)}";
        }
        /// <summary>
        /// 邮箱脱敏
        /// </summary>
        /// <param name="input"></param>
        /// <param name="mask"></param>
        /// <returns></returns>
        public static string MaskEmail(this string input, string mask = "****")
        {
            if (input.IsNull())
            {
                return input;
            }

            return Regex.Replace(input, "(?<=.{2})[^@]+(?=.{2}@)", mask);
        }

        /// <summary>
        /// IP脱敏
        /// </summary>
        /// <param name="input"></param>
        /// <param name="mask"></param>
        /// <returns></returns>
        public static string MaskIP(this string input, string mask = "*")
        {
            if (input.IsNull())
            {
                return input;
            }

            return Regex.Replace(input, @"(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})", match => $"{match.Groups[1]?.Value}.{mask}.{mask}.{match.Groups[4]?.Value}");
        }
        /// <summary>
        /// 根据身份证号获取生日
        /// </summary>
        /// <param name="identityCard"></param>
        /// <returns></returns>
        public static DateTime? GetBirthdayForIdCard(this string identityCard)
        {
            if (identityCard.IsNullOrEmpty() || (identityCard.Length != 15 && identityCard.Length != 18)) return null;
            DateTime? Birthday = null;
            if (identityCard.Length == 18)//处理18位的身份证号码从号码中得到生日和性别代码
            {
                //生日
                Birthday = $"{identityCard.Substring(6, 4)}-{identityCard.Substring(10, 2)}-{identityCard.Substring(12, 2)}".ToDateTime();
            }

            if (identityCard.Length == 15)
            {
                //生日
                Birthday = $"19{identityCard.Substring(6, 2)}-{identityCard.Substring(8, 2)}-{identityCard.Substring(10, 2)}".ToDateTime();
            }
            return Birthday;
        }
        /// <summary>
        /// 根据身份证号获取生日年龄性别
        /// </summary>
        /// <param name="identityCard"></param>
        /// <returns></returns>
        public static int GetAgeForIdCard(this string identityCard)
        {
            var birthday = identityCard.GetBirthdayForIdCard();
            //计算年龄
            var Age = DateTime.Today.Year - birthday.Value.Year;
            if (DateTime.Today.Month < birthday.Value.Month) Age -= 1;
            return Age;
        }
        /// <summary>
        /// 根据身份证号获取生日年龄性别
        /// </summary>
        /// <param name="identityCard"></param>
        /// <returns></returns>
        public static Gender GetBirthdayAgeSex(this string identityCard)
        {
            if (identityCard.IsNullOrEmpty() || (identityCard.Length != 15 && identityCard.Length != 18)) return Gender.UnKnown;
            var num = identityCard.Sub(-4).SafeSubstring(0, 3).To<int>();
            return num % 2 == 0 ? Gender.Girl : Gender.Boy;
        }
        /// <summary>
        /// 用*号隐藏部分用户名、手机号
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public static string ConverUserNameToAnonymous(this string userName)
        {
            string userNameAfterReplaced;
            try
            {
                int nameLength = userName.Length;
                if (nameLength <= 2 && nameLength >= 1)
                {
                    if (nameLength == 1)
                    {
                        userNameAfterReplaced = "*";
                    }
                    else
                    {
                        userNameAfterReplaced = "**";
                    }
                }
                else
                {
                    int num1, num2, num3;
                    num2 = (int)Math.Ceiling((double)nameLength / 3);
                    num1 = (int)Math.Floor((double)nameLength / 3);
                    num3 = nameLength - num1 - num2;
                    StringBuilder starStr = new StringBuilder();
                    for (int i = 0; i < num2; i++)
                    {
                        starStr.Append("*");
                    }
                    userNameAfterReplaced = $"{userName.Substring(0, num1)}{starStr}{userName.Substring(num1 + num2, num3)}";
                }
            }
            catch (Exception)
            {
                userNameAfterReplaced = userName;
            }
            return userNameAfterReplaced;
        }


        /// <summary>  
        /// 验证密码是否符合要求  
        /// </summary>  
        /// <param name="input"></param>  
        /// <returns></returns>  
        public static bool VerifyPassword(this string input)
        {
            if (input.IsNull())
            {
                return false;
            }

            return Regex.IsMatch(input, PasswordRegex);
        }
        public static readonly string PasswordRegex = @"^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d!@#$%^&.*]{6,16}$";
        #endregion

    }
}
